package com.xhl.browser;

import com.xhl.core.authentication.SmsCodeAuthenticationSecurityConfig;
import com.xhl.core.properties.SecurityConstantsProperties;
import com.xhl.core.properties.base.SecurityProperties;
import com.xhl.core.validate.ValidateCodeSecurityConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.DelegatingPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder;
import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.social.security.SpringSocialConfigurer;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

/**
 * @author 徐宏亮
 * @description 继承框架的适配器
 * Spring security 过滤器链有很多过滤器，完成很多认证
 * 1. UsernamePasswordAuthenticationFilter 处理表单登陆过滤器
 * 2. BasicAuthenticationFilter 处理 http basic 登陆过滤器
 * ...... 很多其它过滤器
 * 3. ExceptionTranslationFilter 捕获抛出的异常做相应的处理，如没有登陆，权限不够等
 * 4. FilterSecurityInterceptor 过滤器链的最后一环，决定当前请求能不能访问 url ，依据我们以下代码的配置
 * @date 2019/3/2 14:41
 */
@Configuration
public class BrowserSecurityConfig extends AbstractChannelSecurityConfig {

    @Autowired
    UserDetailsService customUserServiceImpl;

    @Value("${system.user.password.secret}")
    private String secret = null;

    @Autowired
    private SecurityProperties securityProperties;

    @Autowired
    private ValidateCodeSecurityConfig validateCodeSecurityConfig;

    @Autowired
    private SmsCodeAuthenticationSecurityConfig smsCodeAuthenticationSecurityConfig;

    @Autowired
    private SpringSocialConfigurer socialSecurityConfig;

    @Autowired
    private DataSource dataSource;

    public PersistentTokenRepository persistentTokenRepository() {
        JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
        jdbcTokenRepository.setDataSource(dataSource);
        jdbcTokenRepository.setCreateTableOnStartup(true);//创建表的语句
        return jdbcTokenRepository;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        applyPasswordAuthenticationConfig(http);

        http
                .apply(validateCodeSecurityConfig)
                    .and()
                .apply(smsCodeAuthenticationSecurityConfig)
                    .and()
                .apply(socialSecurityConfig)
                    .and()
                .authorizeRequests()
                .antMatchers(
                        SecurityConstantsProperties.DEFAULT_UNAUTHENTICATION_URL,
                        SecurityConstantsProperties.DEFAULT_LOGIN_PROCESSING_URL_MOBILE,
                        securityProperties.getBrowser().getLoginPage(),
                        SecurityConstantsProperties.DEFAULT_VALIDATE_CODE_URL_PREFIX + "/*")
                .permitAll()
                .anyRequest()
                .authenticated()
                    .and()
                .rememberMe()
                .tokenRepository(persistentTokenRepository())
                .tokenValiditySeconds(securityProperties.getBrowser().getRememberMeSeconds())
                .userDetailsService(customUserServiceImpl);
        /*
        http.formLogin() //表单登陆
                .and() //权限配置
                .authorizeRequests() //对请求授权
                .anyRequest() //任何请求
                .authenticated(); //都要身份认证
                */
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 数据库中保存的是，转码后的，所以用户注册时，保存密码要转码
        // auth.userDetailsService(customUserService()).passwordEncoder(new BCryptPasswordEncoder());
        // auth.userDetailsService(customUserService()).passwordEncoder(new Pbkdf2PasswordEncoder(secret));
        String idForEncode = "pbkdf2";
        Map<String, PasswordEncoder> encoders = new HashMap<>(16);
        encoders.put("bcrypt", new BCryptPasswordEncoder());
        encoders.put("pbkdf2", new Pbkdf2PasswordEncoder(secret));
        encoders.put("scrypt", new SCryptPasswordEncoder());
        //encoders.put("noop", NoOpPasswordEncoder.getInstance());
        //encoders.put("sha256", new StandardPasswordEncoder());
        PasswordEncoder passwordEncoder = new DelegatingPasswordEncoder(idForEncode, encoders);
        //这里选择了 pbkdf2 ，并且赋予了钥匙
        //我们使用了 DelegatingPasswordEncoder 数据库就需要带 {id}******
        //数据库中密码为 {pbkdf2}a82c587bab23d062aba9a1c92ad23145fc1d8ccd6b8fcdf5130770e98b102275d54ea3fb236ac813
        auth.userDetailsService(customUserServiceImpl).passwordEncoder(passwordEncoder);
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        super.configure(web);
        //解决静态资源被拦截的问题
        web.ignoring().antMatchers("/home/**");
    }
}
